/*===========================================================================
  Copyright (C) 2012-2013 by the Okapi Framework contributors
-----------------------------------------------------------------------------
  This library is free software; you can redistribute it and/or modify it 
  under the terms of the GNU Lesser General Public License as published by 
  the Free Software Foundation; either version 2.1 of the License, or (at 
  your option) any later version.

  This library is distributed in the hope that it will be useful, but 
  WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
  General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License 
  along with this library; if not, write to the Free Software Foundation, 
  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

  See also the full LGPL text here: http://www.gnu.org/copyleft/lesser.html
===========================================================================*/

package net.sf.okapi.applications.lynx;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.namespace.QName;

import net.sf.okapi.lib.xliff2.Const;
import net.sf.okapi.lib.xliff2.core.CTag;
import net.sf.okapi.lib.xliff2.core.CanReorder;
import net.sf.okapi.lib.xliff2.core.ExtContent;
import net.sf.okapi.lib.xliff2.core.ExtElement;
import net.sf.okapi.lib.xliff2.core.ExtElements;
import net.sf.okapi.lib.xliff2.core.Fragment;
import net.sf.okapi.lib.xliff2.core.Part;
import net.sf.okapi.lib.xliff2.core.Part.GetTarget;
import net.sf.okapi.lib.xliff2.core.Segment;
import net.sf.okapi.lib.xliff2.core.Skeleton;
import net.sf.okapi.lib.xliff2.core.StartFileData;
import net.sf.okapi.lib.xliff2.core.TagType;
import net.sf.okapi.lib.xliff2.core.Unit;
import net.sf.okapi.lib.xliff2.document.XLIFFDocument;
import net.sf.okapi.lib.xliff2.its.ITSWriter;
import net.sf.okapi.lib.xliff2.its.LocQualityIssue;
import net.sf.okapi.lib.xliff2.processor.DefaultEventHandler;
import net.sf.okapi.lib.xliff2.processor.XLIFFProcessor;
import net.sf.okapi.lib.xliff2.reader.Event;
import net.sf.okapi.lib.xliff2.reader.EventType;
import net.sf.okapi.lib.xliff2.reader.XLIFFReader;
import net.sf.okapi.lib.xliff2.writer.XLIFFWriter;

public class Snippets {
	
	public void execute (int num,
		boolean showSource) throws IOException
	{
		System.out.print(String.format("Example %d: ", num));
		if ( num > 16 ) {
			throw new RuntimeException(String.format("There is no example %d.", num));
		}

		// Show source code if required
		if ( showSource ) {
			System.out.println("Source code:");
			BufferedReader reader = null;
			try {
				// Open the source file
				reader = new BufferedReader(new InputStreamReader(
					getClass().getResourceAsStream("/Snippets.java"), "UTF-8"));
				// Read the content 
				StringBuilder text = new StringBuilder();
				String line = null;
				while ( (line = reader.readLine()) != null ) {
					text.append(line+"\n");
				}
				// Fetch the example
				Pattern pattern = Pattern.compile(
					String.format("(/\\* Start example %d \\*/)(.*?)(/\\* End \\*/)", num), Pattern.DOTALL);
				Matcher match = pattern.matcher(text.toString());
				// Display it
				if ( !match.find() ) {
					System.out.println("Source code not found.");
				}
				else {
					System.out.println(match.group(2));
				}
			}
			finally {
				if ( reader != null ) reader.close(); 
			}
		}
		
		// Execute the example
		switch ( num ) {
		case 1: example01(true); return;
		case 2: example02(true); return;
		case 3: example03(true); return;
		case 4: example04(true); return;
		case 5: example05(true); return;
		case 6: example06(true); return;
		case 7: example07(true); return;
		case 8: example08(true); return;
		case 9: example09(true); return;
		case 10: example10(true); return;
		case 11: example11(true); return;
		case 12: example12(true); return;
		case 13: example13(true); return;
		case 14: example14(true); return;
		case 15: example15(true); return;
		case 16: example16(true); return;
		}
	}
	
	public void list () {
		System.out.print("Example 1: "); example01(false);
		System.out.print("Example 2: "); example02(false);
		System.out.print("Example 3: "); example03(false);
		System.out.print("Example 4: "); example04(false);
		System.out.print("Example 5: "); example05(false);
		System.out.print("Example 6: "); example06(false);
		System.out.print("Example 7: "); example07(false);
		System.out.print("Example 8: "); example08(false);
		System.out.print("Example 9: "); example09(false);
		System.out.print("Example 10: "); example10(false);
		System.out.print("Example 11: "); example11(false);
		System.out.print("Example 12: "); example12(false);
		System.out.print("Example 13: "); example13(false);
		System.out.print("Example 14: "); example14(false);
		System.out.print("Example 15: "); example15(false);
		System.out.print("Example 16: "); example16(false);
	}

	/* Start example 1 */
	public void example01 (boolean execute) {
		System.out.println("Creates a \"Hello World!\" document with \"World!\" in HTML bold.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("u1");
			Fragment frag = unit.appendSegment().getSource();
			frag.append("Hello ");
			frag.openCodeSpan("c1", "<b>");
			frag.append("World!");
			frag.closeCodeSpan("c1", "</b>");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 2 */
	public void example02 (boolean execute) {
		System.out.println("Creates a document with some special characters, including an XML invalid character.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("u1");
			Fragment frag = unit.appendSegment().getSource();
			frag.append("&=ampersand, <=less-than, \u0001=U+0001");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */
	
	/* Start example 3 */
	public void example03 (boolean execute) {
		System.out.println("Creates a document with source and target.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en", "fr");
			Unit unit = new Unit("u1");
			Segment seg = unit.appendSegment();
			seg.getSource().append("The text");
			seg.getTarget(GetTarget.CREATE_EMPTY).append("Le texte");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */
	
	/* Start example 4 */
	public void example04 (boolean execute) {
		System.out.println("Creates a document with several segments,\n  then read the created document back and shows some of its parsed content.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en", "fr");
			Unit unit = new Unit("u1");
			unit.appendSegment().setSource("Sentence 1.");
			unit.appendIgnorable().setSource(" ");
			unit.appendSegment().setSource("Sentence 2.");
			writer.writeUnit(unit);
		}
		// Display the document
		System.out.println(sw.toString());
		// Use the created document as the input for the reader
		XLIFFReader reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL);
		reader.open(sw.toString());
		System.out.println("Using the input above with the reader we get:");
		while ( reader.hasNext() ) {
			Event event = reader.next();
			switch ( event.getType() ) {
			case START_XLIFF:
				System.out.println("-Start of <xliff>");
				break;
			case END_XLIFF:
				System.out.println("-End of <xliff>");
				break;
			case START_FILE:
				System.out.println("-Start of <file>");
				break;
			case END_FILE:
				System.out.println("-End of <file>");
				break;
			case TEXT_UNIT:
				Unit unit = event.getUnit();
				System.out.println(String.format("-Unit: id=%s", unit.getId()));
				System.out.println(String.format("--parts (%d):", unit.getPartCount()));
				for ( Part part : unit ) {
					if ( part.isSegment() ) {
						System.out.println(String.format("---segment, source=\"%s\"", part.getSource().toString()));
					}
					else {
						System.out.println(String.format("---ignorable, source=\"%s\"", part.getSource().toString()));
					}
				}
				// Join all parts and show the result
				unit.join(0, -1, true, true, true);
				System.out.println(String.format("--joined source=\"%s\"", unit.getPart(0).getSource().toString()));
				break;
			default:
				break;
			}
		}
		reader.close();
	}
	/* End */

	/* Start example 5 */
	public void example05 (boolean execute) {
		System.out.println("Creates a document, read it and re-write it.");
		if ( !execute ) return;
		System.out.println();
		XLIFFWriter writer = new XLIFFWriter();
		StringWriter sw = new StringWriter();
		writer.create(sw, "fr", "de");
		Unit unit = new Unit("u1");
		Fragment frag = unit.appendSegment().getSource();
		frag.append("Phrase 1 avec ");
		frag.openCodeSpan("1", "<B>");
		frag.append("du gras.");
		frag.closeCodeSpan("1", "</B>");
		unit.appendIgnorable().setSource(" ");
		unit.appendSegment().setSource("Phrase 2.");
		writer.writeUnit(unit);
		writer.close();
		// Display the document
		System.out.println(sw.toString());
		// Use the created document as the input for the reader
		XLIFFReader reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL);
		reader.open(sw.toString());
		// Create the writer
		writer = new XLIFFWriter();
		writer.create(new PrintWriter(System.out), null);
		// Read each event, and write it
		while ( reader.hasNext() ) {
			writer.writeEvent(reader.next());
		}
		writer.close();
		reader.close();
	}
	/* End */

	/* Start example 6 */
	public void example06 (boolean execute) {
		System.out.println("Creates a document with a skeleton.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en", "fr");
			// Create the skeleton
			Skeleton sd = new Skeleton();
			sd.addChild(new ExtContent("Content of the skeleton"));
			writer.writeSkeleton(sd);
			// Create a unit
			Unit unit = new Unit("u1");
			Segment seg = unit.appendSegment();
			seg.setSource("Text.");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */
	
	/* Start example 7 */
	public void example07 (boolean execute) {
		System.out.println("Creates a document with extensions using custom namespaces.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			// Create the file
			StartFileData startFileData = new StartFileData("f1");
			startFileData.getExtAttributes().setNamespace("x1", "ns1");
			startFileData.getExtAttributes().setAttribute("ns1", "extAttr1", "value1");
			writer.writeStartFile(startFileData);
			// Create a unit
			Unit unit = new Unit("u1");
			unit.getExtAttributes().setAttribute("ns1", "extAttr2", "value");
			Segment seg = unit.appendSegment();
			seg.setSource("Source text.");
			// Add an extension element
			unit.setExtElements(new ExtElements())
				.add(new ExtElement(new QName("ns2", "myElement", "x2")))
					.addChild(new ExtContent("The content of the extension element."));
			// Write the unit and close the document
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 8 */
	public void example08 (boolean execute) {
		System.out.println("Splits a segment into several.");
		if ( !execute ) return;
		System.out.println();
		String text = "<?xml version='1.0'?>\n<xliff version=\"2.0\" xmlns=\"urn:oasis:names:tc:xliff:document:2.0\""
			+ "\n srcLang=\"en\" trgLang=\"fr\">\n"
			+ "<file id='myFile'>\n"
			+ "<unit id='1'>\n"
			+ "<segment><source>Text1 <pc id='1'>bold text2</pc> text3.</source>\n"
			//                  Text1 ##bold text2## text3.
			//                  012345678901234567890123456
			+ "</segment>\n"
			+ "</unit></file></xliff>";
		try ( XLIFFReader reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL) ) {
			reader.open(text);
			while ( reader.hasNext() ) {
				Event event = reader.next();
				if ( event.getType() == EventType.TEXT_UNIT ) {
					Unit unit = event.getUnit();
					System.out.println(String.format("Before split:\n<source>%s</source>",
						unit.getPart(0).getSource().toXLIFF()));
					unit.split(0, 0, 13, -1, -1, true);
					System.out.println("After split:");
					for ( Segment seg : unit.getSegments() ) {
						System.out.println(String.format("<source>%s</source>", 
							seg.getSource().toXLIFF()));
					}
				}
			}
		}
	}
	/* End */

	/* Start example 9 */
	public void example09 (boolean execute) {
		System.out.println("Creates an document with just one space.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("1");
			unit.appendIgnorable().setSource(" ");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */
	
	/* Start example 10 */
	public void example10 (boolean execute) {
		System.out.println("Adds a target that takes the same xml:space value as the source.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en", "fr");
			Unit unit = new Unit("1");
			Segment seg = unit.appendSegment();
			seg.setSource("[  ]");
			seg.setPreserveWS(true);
			seg.setTarget("");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 11 */
	public void example11 (boolean execute) {
		System.out.println("Creates two <ec> elements, one isolated, one not.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("1");
			Fragment frag = unit.appendSegment().getSource();
			// Add a paired code
			frag.openCodeSpan("1", "<b>");
			// Add an annotation to force the <pc></pc> element to be output as <sc/>/<ec/>
			frag.openMarkerSpan("m1", "term");
			frag.append("isolated");
			frag.closeCodeSpan("1", "</b>");
			frag.append("code");
			frag.closeMarkerSpan("m1");
			// Add the lone end-code
			frag.insert(TagType.CLOSING, null, "2", "</em>", -1, true, true);
			// Add the lone start-code
			frag.insert(TagType.OPENING, null, "3", "<u>", -1, true, true);
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 12 */
	public void example12 (boolean execute) {
		System.out.println("Sets automatically editing hints of <sc>/<ec> elements.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("1");
			Fragment frag = unit.appendSegment().getSource();
			CTag cm = frag.openCodeSpan("1", "[1]");
			cm.setCanReorder(CanReorder.FIRSTNO);
			frag.append("word1 ");
			cm = frag.openCodeSpan("2", "[2]");
			cm.setCanReorder(CanReorder.NO);
			// Add the closing markers, without setting the hints
			// Or setting them incorrectly for canOverlap
			frag.closeCodeSpan("1", "[/1]");
			frag.append("word2");
			frag.closeCodeSpan("2", "[/2]");
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 13 */
	public void example13 (boolean execute) {
		System.out.println("Output state and subState of segments.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("1");
			Segment seg = unit.appendSegment();
			seg.setSource("text");
			seg.setSubState("my:value");
			// We do not set a state: the default will be output if there is a subState
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 14 */
	public void example14 (boolean execute) {
		System.out.println("Generates an error because a subType is without a type.");
		if ( !execute ) return;
		System.out.println();
		StringWriter sw = new StringWriter();
		try ( XLIFFWriter writer = new XLIFFWriter() ) {
			writer.create(sw, "en");
			Unit unit = new Unit("1");
			Fragment frag = unit.appendSegment().getSource();
			CTag cm = frag.appendCode("1", "code");
			cm.setSubType("my:value");
			// We do not set the type: That will cause an error 
			writer.writeUnit(unit);
		}
		System.out.println(sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 15 */
	private class MyEventHandler extends DefaultEventHandler {
		@Override
		public Event handleUnit (Event event) {
			Unit unit = event.getUnit();
			Fragment fragment = unit.getPart(unit.getPartCount()-1).getSource();
			fragment.setCodedText(fragment.getCodedText()+"_new text");
			return event;
		}
	}
	public void example15 (boolean execute) {
		System.out.println("Use the XLIFFProcessor class.");
		if ( !execute ) return;
		System.out.println();
		// Create the processor object and set the event handler
		XLIFFProcessor processor = new XLIFFProcessor();
		processor.setHandler(new MyEventHandler());
		// Set the input
		String text = "<xliff version=\"2.0\" xmlns=\"urn:oasis:names:tc:xliff:document:2.0\""
			+ "\n srcLang=\"en\" trgLang=\"fr\">\n"
			+ "<file id='myFile'>\n"
			+ "<unit id='1'>\n"
			+ "<segment><source>Text1 <pc id='1'>bold text2</pc> text3.</source>\n"
			+ "</segment>\n"
			+ "</unit></file></xliff>";
		processor.setInput(text);
		System.out.println("Before:\n"+text);
		// Set the output (for the example we output to a string)
		StringWriter sw = new StringWriter();
		processor.setOutput(sw);
		// Execute the modifications
		processor.run();
		// Look and check the output
		System.out.println("After:\n"+sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

	/* Start example 16 */
	public void example16 (boolean execute) {
		System.out.println("Set ITS Localization Quality Issues annotations.");
		if ( !execute ) return;
		System.out.println();
		String text = "<?xml version='1.0'?>\n<xliff version=\"2.0\" xmlns=\"urn:oasis:names:tc:xliff:document:2.0\""
			+ "\n srcLang='en' trgLang='fr'>\n"
			+ "<file id='myFile'>\n"
			+ "<unit id='u1'>\n"
			+ "<segment><source>This is an example.</source>\n"
			+ "<target>c'est un example.</target>"
			//         01234567890123456
			+ "</segment>\n"
			+ "</unit></file></xliff>";
		XLIFFDocument doc = new XLIFFDocument();
		doc.load(text, XLIFFReader.VALIDATION_MAXIMAL);
		doc.getStartXliffData().setNamespace(Const.PREFIX_ITS, Const.NS_XLIFF_ITS21); // Adds the ITS namespace
		doc.getStartXliffData().getExtAttributes().setAttribute(Const.NS_XLIFF_ITS21, "version", "2.0"); // Add ITS version
		Unit unit = doc.getUnitNode("myFile", "u1").get();
		// Do the annotation on the target
		Fragment frag = unit.getPart(0).getTarget();
		LocQualityIssue lqi = (LocQualityIssue)ITSWriter.annotate(frag, 9, 16, new LocQualityIssue("Should be 'exemple'"));
		lqi.setType("misspelling");
		// Create the output and validate it
		StringWriter sw = new StringWriter();
		doc.save(sw);
		System.out.println("After:\n"+sw.toString());
		XLIFFReader.validate(sw.toString(), null);
	}
	/* End */

}
